package ann;

import java.util.HashMap;
import java.util.Iterator;

import topology.Topology;

import learning.LearningRule;

public class Link implements Iterable<Arc> {
	
	private HashMap<Edge, Arc> arcs;
	private Module fromModule;
	private Module toModule;
	private double minInitialWeight;
	private double maxInitialWeight;
	private double learningRate;
	private LearningRule learningRule;
	private double momentumRate;
	
	private Link(Module from, Module to, Topology topology,LearningRule learningRule,double learningRate,double minInitialWeight,double maxInitialWeight, double momentumRate) {
		this.minInitialWeight = minInitialWeight;
		this.maxInitialWeight = maxInitialWeight;
		this.learningRule = learningRule;
		this.learningRate = learningRate;
		arcs = new HashMap<Edge, Arc>(); 
		fromModule = from;
		toModule = to;
		this.momentumRate = momentumRate;
		
		assert(topology!=null);
		
		if(!topology.isReady())
			topology.setup(from.getOutputSize(), to.getInputSize());
		
		for(Edge edge : topology)
		{
			assert(edge.from<from.getOutputSize()):(from.getOutputSize()+", "+edge.from);
			assert(edge.to<to.getInputSize());
			arcs.put(edge, new Arc(this,edge.from,edge.to,randomWeight()));
		}
		
	}

	public Iterator<Arc> iterator() {
		return arcs.values().iterator();
	}

	public Module getFromModule() {
		return fromModule;
	}

	public Module getToModule() {
		return toModule;
	}

	public void incrementWeightDeltas(){
		
		Module fromLayer = fromModule.getLastLayer();
		Module toLayer = toModule.getFirstLayer();
		
		for(Arc arc : arcs.values())
		{
			int from = arc.getFromIndex();
			int to = arc.getToIndex();
			double increment = learningRule.getDeltaWeight(fromLayer.getOutput(from), toLayer.getOutput(to), learningRate, arc.getWeight(),toLayer.getInError(to));
			arc.incrementWeightDelta(increment);
		}
	}

	public void addWeightDeltas() {
		for(Arc arc : arcs.values())
		{
			arc.addWeightDelta();
		}
	}
	public String toString()
	{
		String ret = "";
		
		ret+="Link { LearningRule: "+learningRule.toString()+"\n";
		
		for(int i=0;i<fromModule.getOutputSize();i++)
			for(int j=0;j<toModule.getInputSize();j++)
			{
				if(arcs.containsKey(new Edge(i,j))){
					ret+="\t"+arcs.get(new Edge(i,j))+"\n";
				}
			}	
		
		ret+="}\n";
		return ret;
	}
	
	private double tr(double val){
		return ((int)(val*10000))/10000.0;
	}
	
	private double randomWeight(){
		return Math.random()*(maxInitialWeight-minInitialWeight) + minInitialWeight;
	}

	public void setWeight(int from, int to, double weight) {
		arcs.get(new Edge(from,to)).setWeight(weight);
	}

	public double getMomentumRate() {
		return momentumRate;
	}

	public static Link setupLink(Module from, Module to,
			Topology topology, LearningRule learningRule,
			double learningRate, double minInitialWeight,
			double maxInitialWeight, double momentumRate) {
		Link link = new Link(from,to,topology,learningRule,learningRate,minInitialWeight,maxInitialWeight,momentumRate);
		from.addOutputLink(link);
		to.addInputLink(link);
		return link;
	}
}
